package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

import core.Phrox;

public class NetIO {



	public static void write(Connection c, Message m) throws DisconnectionException{
		synchronized(c){
			if(c.connectionStatus == -1){
				throw new DisconnectionException();
			}
			if(NetworkVars.showData){
				System.out.println("OUT: "+m.getText());
			}
			if(c.encP != null){//encrypt only the contents of the msg.
				m.setText( c.encP.encrypt(m.getText()));
			}
			if(NetworkVars.showData){
				System.out.println("OUT: "+m.getText());
			}
			c.stdOut.print(""+Message.convertMessage(m)+""+((char)Message.DELIMITER));
		}
	}

	public static Message read(Connection c) throws DisconnectionException, IOException{
		Message ret=null;
		synchronized (c) {
			if(c.connectionStatus == -1){
				throw new DisconnectionException();
			}
			ret = null;
			while(!c.msgIn.ready());
			String in = c.msgIn.readMessage();
			try {
				ret = Message.parseMessage(in);
			} catch (MessageFormatException e) {}
			if(NetworkVars.showData){
				System.out.println("IN: "+ret.getText());
			}
			if(c.key != null){
				ret.setText( c.encP.decrypt(ret.getText()) );
			}
			if(NetworkVars.showData){
				System.out.println("IN: "+ret.getText());
			}
		}
		ret.orig = c;
		return ret;
	}


	/**
	 * Used for disconnecting a single client
	 * @param c The connection that is to be disconnected.
	 * @throws DisconnectionException If we try to disconnect an already disconnected connection. 
	 * @throws IOException If an IO error occurs while writing to or reading from the connection.
	 */
	public static boolean disconnect(Connection c) throws DisconnectionException, IOException{
		if(c.connectionStatus != 1){//if were not connected, (we're disconnecting, or unconnected), throw exception
			throw new DisconnectionException("Already disconnected");
		}
		c.connectionStatus = 0;
		Message in=new Message(Message.CONNECTION_MSG,"")
		, out=new Message(Message.CONNECTION_MSG,"");
		if(!c.localDisconnect){
			//we didn't start the disconnect, a 'RD' has already arrived
			in.setText(NetworkingProtocol.DISCONNECT);
		}

		while( c.netProt.getState() != NetworkingProtocol.WAITING || out.getText().equals("ERROR") ){

			out = new Message(Message.CONNECTION_MSG, c.netProt.protocol( in.getText() ) );
			write(c,out);

			do{
				in = read(c);//drop all messages that arn't connection msgs.
			}while(in.getType() != Message.CONNECTION_MSG);

			if(NetworkVars.showData){
				System.out.println(in.getText());
			}
		}
		c.connectionStatus = -1;
		c.close();
		return true;
	}

	public static boolean dropConnection(Connection c) throws DisconnectionException {
		if(c.connectionStatus != 1){//if were not connected, (we're disconnecting, or unconnected), throw exception
			throw new DisconnectionException("Already disconnected");
		}
		c.connectionStatus = 0;
		c.netProt.reset();
		//gotta reset the connection protocol
		core.Phrox.outln("DISCONNECTED");
		c.connectionStatus = -1;
		c.close();
		return true;
	}

	public static Connection connect(InetAddress inet, int port, boolean server) {
		Connection con = null;
		try {
			Socket soc = new Socket(inet,port);
			soc.setKeepAlive(true);
			if(NetworkVars.debug){
				System.out.println("Connecting to:"+inet.getHostAddress());
			}

			InputStream in = soc.getInputStream();
			OutputStream out = soc.getOutputStream();
			NetworkingProtocol net = null;
			net = new NetworkingProtocol(NetworkVars.name, (NetworkVars.prvKey!=null) );
			
			
			if( !server ){
				out.write( net.connection_protocol_send().getBytes() );
			}

			while( net.getState() != NetworkingProtocol.CONNECTED){

				while( in.available() >= 0);//wait for input
				byte[] b = new byte[in.available()];
				int i = in.read(b);//read input
				String str = new String(b,0,i);//create teh string

				if( net.connection_protocol(str) ){//only if it's valid
					str = net.connection_protocol_send();//get and send output from protocol
					out.write(str.getBytes());
				}else{
					str = net.connection_protocol_send();//get and send output from protocol, will be error
					out.write(str.getBytes());
					break;
				}

			}//end while loop (state==connected)

			if( net.getState() == NetworkingProtocol.CONNECTED ){
				con = Connection.buildConnection(soc, net.getKey(), new User(inet,""));
				con.aliveNow();
				con.connectionStatus = 1;
			}

		} catch (IOException e) {
			if(NetworkVars.debug){
				System.out.println("Couldn't connect to:"+inet.getHostName());
				e.printStackTrace();
			}
		}
		return con;
	}








}
